switchroot: Further work on being dracut-only
authorColin Walters <walters@verbum.org>
Wed, 8 Feb 2012 22:32:34 +0000 (17:32 -0500)
committerColin Walters <walters@verbum.org>
Wed, 8 Feb 2012 22:32:34 +0000 (17:32 -0500)
src/switchroot/ostree-switch-root.c

index 16be2b8ebf0db380d47f2362b9e965f3e917bcb9..f02edd8f48a3968e572a4e9debf2b9046e1d800a 100644 (file)
@@ -1,7 +1,14 @@
 /* -*- c-file-style: "gnu" -*-
  * Switch to new root directory and start init.
+ * 
  * Copyright 2011,2012 Colin Walters <walters@verbum.org>
  *
+ * Based on code from util-linux/sys-utils/switch_root.c, 
+ * Copyright 2002-2009 Red Hat, Inc.  All rights reserved.
+ * Authors:
+ *     Peter Jones <pjones@redhat.com>
+ *     Jeremy Katz <katzj@redhat.com>
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -57,6 +64,89 @@ perrorv (const char *format, ...)
   return 0;
 }
 
+/* remove all files/directories below dirName -- don't cross mountpoints */
+static int
+recursive_remove (int fd)
+{
+  struct stat rb;
+  DIR *dir;
+  int rc = -1;
+  int dfd;
+
+  if (!(dir = fdopendir (fd)))
+    {
+      perrorv ("failed to open directory");
+      goto done;
+    }
+
+  /* fdopendir() precludes us from continuing to use the input fd */
+  dfd = dirfd (dir);
+
+  if (fstat(dfd, &rb))
+    {
+      perrorv("failed to stat directory");
+      goto done;
+    }
+
+  while (1)
+    {
+      struct dirent *d;
+
+      errno = 0;
+      if (!(d = readdir (dir)))
+       {
+         if (errno)
+           {
+             perrorv ("failed to read directory");
+             goto done;
+           }
+         break;        /* end of directory */
+       }
+      
+      if (!strcmp (d->d_name, ".") || !strcmp (d->d_name, ".."))
+       continue;
+
+      if (d->d_type == DT_DIR)
+       {
+         struct stat sb;
+         
+         if (fstatat (dfd, d->d_name, &sb, AT_SYMLINK_NOFOLLOW))
+           {
+             perrorv ("failed to stat %s", d->d_name);
+             continue;
+           }
+
+         /* remove subdirectories if device is same as dir */
+         if (sb.st_dev == rb.st_dev)
+           {
+             int cfd;
+
+             cfd = openat (dfd, d->d_name, O_RDONLY);
+             if (cfd >= 0)
+               {
+                 recursive_remove (cfd);
+                 close (cfd);
+               }
+           }
+         else
+           {
+             continue;
+           }
+       }
+
+      if (unlinkat (dfd, d->d_name,
+                   d->d_type == DT_DIR ? AT_REMOVEDIR : 0))
+       perrorv ("failed to unlink %s", d->d_name);
+    }
+
+  rc = 0;      /* success */
+  
+ done:
+  if (dir)
+    closedir (dir);
+  return rc;
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -65,14 +155,17 @@ main(int argc, char *argv[])
   const char *ostree_bind_mounts[] = { "/var", NULL };
   const char *readonly_bind_mounts[] = { "/bin", "/etc", "/lib", "/sbin", "/usr",
                                         NULL };
-  const char *ostree_root = NULL;
+  const char *root_mountpoint = NULL;
+  const char *ostree_target = NULL;
   const char *ostree_subinit = NULL;
   char srcpath[PATH_MAX];
   char destpath[PATH_MAX];
   struct stat stbuf;
   char **init_argv = NULL;
+  int initramfs_fd;
   int i;
   int before_init_argc = 0;
+  pid_t cleanup_pid;
 
   if (argc < 3)
     {
@@ -81,47 +174,86 @@ main(int argc, char *argv[])
     }
 
   before_init_argc++;
-  ostree_root = argv[1];
+  root_mountpoint = argv[1];
   before_init_argc++;
-  ostree_subinit = argv[2];
+  ostree_target = argv[2];
+  before_init_argc++;
+  ostree_subinit = argv[3];
   before_init_argc++;
 
-  snprintf (destpath, sizeof(destpath), "/ostree/%s", ostree_root);
+  snprintf (destpath, sizeof(destpath), "%s/ostree/%s",
+           root_mountpoint, ostree_target);
   if (stat (destpath, &stbuf) < 0)
     {
       perrorv ("Invalid ostree root '%s'", destpath);
       exit (1);
     }
   
-  snprintf (destpath, sizeof(destpath), "/ostree/%s/var", ostree_root);
-  if (mount ("/ostree/var", destpath, NULL, MS_BIND, NULL) < 0)
+  for (i = 0; initramfs_move_mounts[i] != NULL; i++)
     {
-      perrorv ("Failed to bind mount / to '%s'", destpath);
+      const char *path = initramfs_move_mounts[i];
+      snprintf (srcpath, sizeof(srcpath), path);
+      snprintf (destpath, sizeof(destpath), "/ostree/%s%s", ostree_target, path);
+      if (mount (srcpath, destpath, NULL, MS_MOVE, NULL) < 0)
+       {
+         perrorv ("failed to move mount of %s to %s", srcpath, destpath);
+         exit (1);
+       }
+    }
+
+  if (chdir (root_mountpoint) < 0)
+    {
+      perrorv ("failed to chdir to %s", root_mountpoint);
+      exit (1);
+    }
+
+  initramfs_fd = open ("/", O_RDONLY);
+
+  if (mount (root_mountpoint, "/", NULL, MS_MOVE, NULL) < 0)
+    {
+      perrorv ("failed move %s to /", root_mountpoint);
+      return -1;
+    }
+
+  if (chroot (".") < 0)
+    {
+      perrorv ("failed to chroot to .");
       exit (1);
     }
   
-  snprintf (destpath, sizeof(destpath), "/ostree/%s/sysroot", ostree_root);
+  if (initramfs_fd >= 0)
+    {
+      cleanup_pid = fork ();
+      if (cleanup_pid == 0)
+       {
+         recursive_remove (initramfs_fd);
+         exit (0);
+       }
+      close (initramfs_fd);
+    }
+
+  /* From this point on we're chrooted into the real root filesystem,
+   * so we no longer refer to root_mountpoint.
+   */
+  
+  snprintf (destpath, sizeof(destpath), "/ostree/%s/sysroot", ostree_target);
   if (mount ("/", destpath, NULL, MS_BIND, NULL) < 0)
     {
       perrorv ("Failed to bind mount / to '%s'", destpath);
       exit (1);
     }
 
-  for (i = 0; initramfs_move_mounts[i] != NULL; i++)
+  snprintf (srcpath, sizeof(srcpath), "%s", "/ostree/var");
+  snprintf (destpath, sizeof(destpath), "/ostree/%s/var", ostree_target);
+  if (mount (srcpath, destpath, NULL, MS_BIND, NULL) < 0)
     {
-      const char *path = initramfs_move_mounts[i];
-      snprintf (srcpath, sizeof(srcpath), path);
-      snprintf (destpath, sizeof(destpath), "/ostree/%s%s", ostree_root, path);
-      if (mount (srcpath, destpath, NULL, MS_MOVE, NULL) < 0)
-       {
-         perrorv ("failed to move mount of %s to %s", srcpath, destpath);
-         exit (1);
-       }
+      perrorv ("Failed to bind mount '%s' to '%s'", srcpath, destpath);
+      exit (1);
     }
 
   for (i = 0; toproot_bind_mounts[i] != NULL; i++)
     {
-      snprintf (destpath, sizeof(destpath), "/ostree/%s%s", ostree_root, toproot_bind_mounts[i]);
+      snprintf (destpath, sizeof(destpath), "/ostree/%s%s", ostree_target, toproot_bind_mounts[i]);
       if (mount (toproot_bind_mounts[i], destpath, NULL, MS_BIND & ~MS_RDONLY, NULL) < 0)
        {
          perrorv ("failed to bind mount (class:toproot) %s to %s", toproot_bind_mounts[i], destpath);
@@ -132,7 +264,7 @@ main(int argc, char *argv[])
   for (i = 0; ostree_bind_mounts[i] != NULL; i++)
     {
       snprintf (srcpath, sizeof(srcpath), "/ostree/%s", ostree_bind_mounts[i]);
-      snprintf (destpath, sizeof(destpath), "/ostree/%s%s", ostree_root, ostree_bind_mounts[i]);
+      snprintf (destpath, sizeof(destpath), "/ostree/%s%s", ostree_target, ostree_bind_mounts[i]);
       if (mount (srcpath, destpath, NULL, MS_MGC_VAL|MS_BIND, NULL) < 0)
        {
          perrorv ("failed to bind mount (class:bind) %s to %s", srcpath, destpath);
@@ -142,7 +274,7 @@ main(int argc, char *argv[])
 
   for (i = 0; readonly_bind_mounts[i] != NULL; i++)
     {
-      snprintf (destpath, sizeof(destpath), "/ostree/%s%s", ostree_root, readonly_bind_mounts[i]);
+      snprintf (destpath, sizeof(destpath), "/ostree/%s%s", ostree_target, readonly_bind_mounts[i]);
       if (mount (destpath, destpath, NULL, MS_BIND, NULL) < 0)
        {
          perrorv ("failed to bind mount (class:readonly) %s", destpath);
@@ -155,7 +287,7 @@ main(int argc, char *argv[])
        }
     }
   
-  snprintf (destpath, sizeof(destpath), "/ostree/%s", ostree_root);
+  snprintf (destpath, sizeof(destpath), "/ostree/%s", ostree_target);
   if (chroot (destpath) < 0)
     {
       perrorv ("failed to change root to '%s'", destpath);